package com.taotao.complier.calcu.parse;

import com.taotao.complier.calcu.ast.AddSubNode;
import com.taotao.complier.calcu.ast.Expr2Node;
import com.taotao.complier.calcu.ast.ExprNode;
import com.taotao.complier.calcu.ast.FactorNode;
import com.taotao.complier.calcu.ast.MulDivNode;
import com.taotao.complier.calcu.ast.NumNode;
import com.taotao.complier.calcu.ast.Term2Node;
import com.taotao.complier.calcu.ast.TermNode;
import com.taotao.complier.calcu.lex.Lexer;
import com.taotao.complier.calcu.lex.Token;
import com.taotao.complier.calcu.lex.WordType;

/**
 * 计算表达式语法器解析器（递归版）
 *
 * @author fanbeibei
 * @date 2020/9/5 13:39
 */
public class CalcuParser implements Parser<ExprNode> {
    private Lexer lex;
    private Token currentToken;

    public CalcuParser(Lexer lex) {
        this.lex = lex;
    }

    /**
     * 当前分词是否匹配该种别码
     *
     * @param wordType
     * @return
     */
    protected boolean match(WordType wordType) {
        if (null == currentToken) {
            // 当前token已被用掉所以匹配判断前要先读下一个
            lex.nextToken();
            currentToken = lex.token();
        }

        return currentToken.getWordType() == wordType;
    }


    @Override
    public ExprNode parse() {
        // 解析为抽象语法树的逻辑，这里要用到产生式
        /**
         * 3 + 4567 * 342432 - 345345 / 34533
         *
         * 产生式
         *
         * 0: expr -> term  expr2
         * 1: expr2 ->  ADD_SUB term  expr2
         * 2:          | ε
         * 3: term ->  factor  term2
         * 4: term2 ->  MUL_DIV factor term2
         * 5:         | ε
         * 6: factor -> num
         * 7: MUL_DIV -> + | -
         * 8: ADD_SUB -> * | /
         *
         * FIRST(expr)={num}
         * FIRST(expr2)={ADD_SUB}
         * FIRST(term)={num}
         * FIRST(term2)={MUL_DIV}
         * FIRST(factor)={num}
         *
         *
         * 预测分析表
         * |      | num | + - | * / |
         * |:----:|:---:|:---:|:---:|
         * | expr |  0  |    |     |
         *
         *
         * | expr2|     |  1  |     |
         * | term |  3  |     |     |
         * | term2|     |    |  4  |
         *
         *
         * | factor|  6  |     |     |
         *
         */

//        lex.nextToken();
        while (!match(WordType.EOF)) {
            // expr -> term  expr2
            TermNode t = term();


            Expr2Node e2 = expr2();

            return new ExprNode(t, e2);
        }


        // 这里永远不会走到
        return null;
    }

    private TermNode term() {

        // 判断匹配规则 ，term 必须以num 开头
        while (match(WordType.NUMBER)) {
            // term ->  factor  term2
            FactorNode f = factor();
            if (null == f) {
                return null;
            }


            Term2Node t2 = term2();


            return new TermNode(f, t2);

        }

        Token token = lex.token();
        throw new IllegalStateException("expected number token   at positon " + lex.pos() + ",actul is " + token.getWordType() + " ,token=" + token);

    }

    private FactorNode factor() {
        // factor -> num
        NumNode n = num();

        return new FactorNode(n);
    }


    private Term2Node term2() {
        // term2 ->  MUL_DIV factor term2 |  ε

        while (match(WordType.MUL) || match(WordType.DIV)) {

            MulDivNode mulDiv = mulDive();
            FactorNode f = factor();
            Term2Node t2 = term2();

            return new Term2Node(mulDiv, f, t2);
        }

        //  ε
        return null;

    }

    private MulDivNode mulDive() {
        if (!match(WordType.MUL) && !match(WordType.DIV)) {
            throw new IllegalStateException("expected number token   at positon " + lex.pos() + ",actul is " + currentToken.getWordType() + " ,token=" + currentToken);
        }

        MulDivNode md = new MulDivNode(currentToken);
        currentToken = null;
        return md;
    }


    private NumNode num() {
        if (!match(WordType.NUMBER)) {
            throw new IllegalStateException("expected number token   at positon " + lex.pos() + ",actul is " + currentToken.getWordType() + " ,token=" + currentToken);
        }

        NumNode n = new NumNode(currentToken);
        currentToken = null;

        return n;
    }

    private Expr2Node expr2() {
        // expr2 ->  ADD_SUB term  expr2  | ε
        while (match(WordType.ADD) || match(WordType.SUB)) {
            AddSubNode as = addSub();

            TermNode t = term();
            Expr2Node e2 = expr2();

            return new Expr2Node(as, t, e2);
        }

        //  ε
        return null;

    }

    private AddSubNode addSub() {
        if (!match(WordType.ADD) && !match(WordType.SUB)) {
            throw new IllegalStateException("expected addSub token   at positon " + lex.pos() + ",actul is " + currentToken.getWordType() + " ,token=" + currentToken);
        }

        AddSubNode as = new AddSubNode(currentToken);
        currentToken = null;
        return as;
    }


}
